// 代码题四、手写实现MyPromise源码

// 由于状态值会被频繁用到，把其定义为常量
const pending = 'pending' // 等待
const fulfilled = 'fulfilled' // 成功
const rejected = 'rejected' // 失败

class MyPromise {
    constructor (executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            // 执行出错的时候，返回错误
            this.reject(e)
        }
    }
    // promise状态， 默认是等待
    status = pending
    // 成功后的值
    value = undefined
    // 失败后的原因
    reason = undefined
    // 成功回调 默认值是数组，可以存储多个函数
    successCallback = []
    // 失败回调 默认值是数组，可以存储多个函数
    failCallback = []

    resolve = value => {
        // 由于状态一旦确认，就不能更改，更改前判断状态如果不是等待，阻止程序向下执行
        if (this.status !== pending) return
        // 将状态更改为成功,并保存成功后的值
        this.status = fulfilled
        this.value = value
        // 实现异步
        // 判断成功回调是否存在 存在才调用
        // this.successCallback && this.successCallback(this.value)
        // 利用shift方法，从前往后逐个执行，每执行一个删除一个，直到数组长度为0
        while(this.successCallback.length) this.successCallback.shift()()
    }
    reject = reason => {
        // 由于状态一旦确认，就不能更改，更改前判断状态如果不是等待，阻止程序向下执行
        if (this.status !== pending) return
        // 将状态更改为失败,并保存失败后的原因
        this.status = rejected
        this.reason = reason
        // 实现异步
        // 判断失败回调是否存在 存在才调用
        // 利用shift方法，从前往后逐个执行，每执行一个删除一个，直到数组长度为0
        // this.failCallback && this.failCallback(this.reason)
        while(this.failCallback.length) this.failCallback.shift()()
    }
    then (successCallback, failCallback) {
        // 根据当前状态，返回对应的回调
        // if (this.status === fulfilled) {
        //     successCallback(this.value)
        // } else if (this.status === rejected) {
        //     failCallback(this.reason)
        // }

        // 参数可选,判断参数是否存在，不存在则直接返回值或者错误信息
        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback: reason => { throw reason }
        // then方法的链式调用需要返回一个promise对象
        let promiseThen = new MyPromise((resolve, reject) => {
          // 判断状态, 无论是成功，失败，还是等待异步，都要调用resolvePromise方法
          if (this.status === fulfilled) {
              // setTimeout异步为了resolvePromise能获取到promiseThen
            setTimeout(() => {
              try {
                  let x = successCallback(this.value)
                  resolvePromise(promiseThen, x, resolve, reject)
              }catch (e) {
                  
                  // 捕捉到错误，需要把错误信息返回给下一个promise函数
                  reject(e)
              }
            }, 0)
          }else if (this.status === rejected) {
            setTimeout(() => {
              try {
                  let x = failCallback(this.reason)
                  resolvePromise(promiseThen, x, resolve, reject)
              }catch (e) {
                  // 捕捉到错误，需要把错误信息返回给下一个promise函数
                  reject(e)
              }
            }, 0)
          } else {
            // 等待
            // 异步的情况，将成功回调和失败回调存储起来，有成功或失败的结棍返回时，再依次调用
            this.successCallback.push(() => {
              setTimeout(() => {
                try {
                    let x = successCallback(this.value)
                    resolvePromise(promiseThen, x, resolve, reject)
                }catch (e) {
                    // 捕捉到错误，需要把错误信息返回给下一个promise函数
                    reject(e)
                }
              }, 0)
            })
            this.failCallback.push(() => {
              setTimeout(() => {
                try {
                  let x = failCallback(this.reason)
                  resolvePromise(promiseThen, x, resolve, reject)
                }catch (e) {
                  reject(e)
                }
              }, 0)
            })
          }
        })
        return promiseThen;
      }
      static resolve (value) {
        // 判断该值是普通值还是Promise对象，如果是Promise对象，直接返回值，如果是普通值，则创建一个Promise对象，通过resolve把值返回
        if (value instanceof MyPromise) return value
        return new MyPromise(resolve => resolve(value))
      }
      // all方法用来解决异步并发问题，是一个静态方法，需要用static声明
      static all (array) {
        let result = []
        let index = 0
        return new MyPromise((resolve, reject) => {
          function addData (i, value) {
            result[i] = value
            index++
            if (index === array.length) {
            // 确保所有项休执行完，才进行resolve
              resolve(result)
            }
          }
          for (let i = 0; i < array.length; i++) {
            let item = array[i]
            // 判断数组中的每个值，如果是普通值，直接放到result中，如果是Promise对象，则先执行promise对象,再把promise的返回值存到result中
            if (item instanceof MyPromise) {
              // promise 对象
              item.then(value => addData(i, value), reason => reject(reason))
            }else {
              // 普通值
              addData(i, array[i])
            }
          }
        })
      }
      // 无论结果是成功还是失败，都调用finally方法
      finally (callback) {
        return this.then(value => {
            // 成功回调
            return MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            // 失败回调
            return MyPromise.resolve(callback()).then(() => { throw reason })
        })
      }
      // 用于处理当前状态为失败状态的情况
      catch (failCallback) {
          // then方法第一个参数是成功的回调，传undefined，catch捕捉的是失败时的状态
          return this.then(undefined, failCallback)
      }
}
function resolvePromise (promiseThen, x, resolve, reject) {
    // 判断 x 的值是普通值还是promise对象
    // 如果是普通值 直接调用resolve 
    // 如果是promise对象 查看promise对象返回的结果 
    // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
    if (promiseThen === x) {
        // 如果返回的是自己，返回错误并阻止代码往下执行
      return reject(new TypeError('Chaining cycle detected for promise'))
    }
    if (x instanceof MyPromise) {
      // promise 对象时，把值返回给下一个then方法调用
      x.then(resolve, reject)
    } else {
      // 普通值直接返回
      resolve(x)
    }
  }
module.exports = MyPromise